from coin2.service.order_detector.common import (OpenOrderElement, QuerySpec)
from coin2.api_client.Binance import (BinancePrivateClient)
from coin2.api_client.Bitstamp import (BitstampPrivateClient)
from coin2.api_client.Bybit import (BybitPrivateClient)
from coin2.api_client.Coincheck import (CoincheckPrivateClient)
from coin2.api_client.Ftx import (FtxPrivateClient)
from coin2.api_client.Gateio import (GateioPrivateClient)
from coin2.api_client.Gdax import (GdaxPrivateClient)
from coin2.api_client.Huobi import (HuobiPrivateClient)
from coin2.api_client.Kucoin import (KucoinPrivateClient)
from coin2.api_client.Mexc import (MexcPrivateClient)
from coin2.api_client.Okex import (OkexPrivateClient)
from coin2.api_client.Prex import (PrexPrivateClient)

import json
import time


def detecotor_open_order_binance(spec: QuerySpec):
  binance = BinancePrivateClient(spec.key_path, spec.mea)
  symbol_size = len(spec.native_symbols)
  if symbol_size >= 10 or symbol_size == 0:
    resp = binance.get_open_order()
    resp = resp.json()
  else:
    resp = []
    for native_symbol in spec.native_symbols:
      res = binance.get_open_order(native_symbol)
      res = res.json()
      resp += res

  orders = []
  timestamp = time.time_ns()
  for item in resp:
    if "orderId" not in item:
      raise Exception(resp)
    ele = OpenOrderElement(item["orderId"], item, timestamp)
    orders.append(ele)

  return orders, resp


def detecotor_open_order_bitstamp(spec: QuerySpec):
  bitstamp = BitstampPrivateClient(spec.key_path, spec.mea, spec.ip)
  resp = bitstamp.get_open_order()
  resp = resp.json()

  orders = []
  timestamp = time.time_ns()
  for item in resp:
    ele = OpenOrderElement(item["id"], item, timestamp)
    orders.append(ele)
  return orders, resp


def detecotor_open_order_bybit(spec: QuerySpec):
  coincheck = BybitPrivateClient(spec.key_path, spec.mea)
  symbols = spec.native_symbols
  orders = []
  resps = {}

  timestamp = time.time_ns()
  for symbol in symbols:
    resp = coincheck.get_open_order(symbol)
    resp = resp.json()
    resps.append(dict({symbol: resp}))
    if "result" not in resp:
      raise Exception(resp)

    result = resp["result"]
    for item in result:
      ele = OpenOrderElement(item["order_id"], item, timestamp)
      orders.append(ele)

  return orders, resps


def detecotor_open_order_coincheck(spec: QuerySpec):
  coincheck = CoincheckPrivateClient(spec.key_path, spec.mea, spec.ip)
  resp = coincheck.get_open_order()
  resp = resp.json()

  if resp["success"] is False:
    raise Exception(json.dumps(resp, indent=2))

  raw = resp["result"]
  orders = []
  timestamp = time.time_ns()
  for item in raw:
    ele = OpenOrderElement(item["id"], item, timestamp)
    orders.append(ele)

  return orders, resp


def detecotor_open_order_gateio(spec: QuerySpec):
  gateio = GateioPrivateClient(spec.key_path, spec.mea)
  resp = gateio.get_open_order()
  resp = resp.json()

  orders = []
  timestamp = time.time_ns()
  for currency_pair in resp:
    if "total" not in currency_pair:
      raise Exception(resp)

    if currency_pair["total"] > 0:
      for item in currency_pair["orders"]:
        item["currency_pair"] = currency_pair["currency_pair"]
        ele = OpenOrderElement(item["id"], item, timestamp)
        orders.append(ele)

  return orders, resp


def detecotor_open_order_ftx(spec: QuerySpec):
  ftx = FtxPrivateClient(spec.key_path, spec.mea, spec.ip)
  resp = ftx.get_open_order()
  resp = resp.json()

  if resp["success"] is False:
    raise Exception(json.dumps(resp, indent=2))

  raw = resp["result"]
  orders = []
  timestamp = time.time_ns()
  for item in raw:
    ele = OpenOrderElement(item["id"], item, timestamp)
    orders.append(ele)

  return orders, resp


def detecotor_open_order_gdax(spec: QuerySpec):
  gdax = GdaxPrivateClient(spec.key_path, spec.mea)
  resp = gdax.get_open_order()
  resp = resp.json()
  orders = []

  timestamp = time.time_ns()
  for item in resp:
    ele = OpenOrderElement(item["id"], item, timestamp)
    orders.append(ele)

  return orders, resp


def detecotor_open_order_huobi(spec: QuerySpec):
  huobi = HuobiPrivateClient(spec.key_path, spec.mea, spec.ip)
  resp = huobi.get_open_order()
  resp = resp.json()

  if resp["status"] != "ok":
    raise Exception(json.dumps(resp, indent=2))

  raw = resp["data"]
  orders = []
  timestamp = time.time_ns()
  for item in raw:
    ele = OpenOrderElement(item["id"], item, timestamp)
    orders.append(ele)

  return orders, resp


def detecotor_open_order_kucoin(resp: QuerySpec):
  kucoin = KucoinPrivateClient(resp.key_path, resp.mea)
  orders = []
  resp = []
  resp = kucoin.get_open_order()
  resp = resp.json()
  itmes = resp["data"]["items"]

  timestamp = time.time_ns()
  for itme in itmes:
    ele = OpenOrderElement(itme["id"], itme, timestamp)
    orders.append(ele)

  return orders, resp


def detecotor_open_order_mexc(resp: QuerySpec):
  mexc = MexcPrivateClient(resp.key_path, resp.mea, resp.ip)
  orders = []
  resps = []
  symbols = resp.native_symbols

  timestamp = time.time_ns()
  for symbol in symbols:
    resp = mexc.get_open_order(symbol)
    resp = resp.json()
    resps.append(dict({symbol: resp}))
    if "data" not in resp:
      raise Exception(resp)

    data = resp["data"]
    for item in data:
      ele = OpenOrderElement(item["id"], item, timestamp)
      orders.append(ele)

  return orders, resps


def detecotor_open_order_okex(resp: QuerySpec):
  okex = OkexPrivateClient(resp.key_path, resp.mea, resp.ip)
  resp = okex.get_open_order()
  resp = resp.json()

  if "data" not in resp:
    raise Exception(resp)

  orders = []
  timestamp = time.time_ns()
  for item in resp["data"]:
    ele = OpenOrderElement(item["ordId"], item, timestamp)
    orders.append(ele)

  return orders, resp


def detecotor_open_order_prex(spec: QuerySpec):
  prex = PrexPrivateClient(spec.key_path, spec.mea)
  resp = prex.get_open_order()
  resp = resp.json()

  if "order" in resp:
    raw = resp["order"]
  else:
    raise Exception(resp)

  timestamp = time.time_ns()
  orders = []
  for item in raw:
    ele = OpenOrderElement(item["orderId"], item, timestamp)
    orders.append(ele)

  return orders, resp


callback_map = {"Binance": detecotor_open_order_binance,
                "Bitstamp": detecotor_open_order_bitstamp,
                "Bybit": detecotor_open_order_bybit,
                "Coincheck": detecotor_open_order_coincheck,
                "Ftx": detecotor_open_order_ftx,
                "Gdteio": detecotor_open_order_gateio,
                "Gdax": detecotor_open_order_gdax,
                "Huobi": detecotor_open_order_huobi,
                "Kucoin": detecotor_open_order_kucoin,
                "Mexc": detecotor_open_order_mexc,
                "Okex": detecotor_open_order_okex,
                "Prex": detecotor_open_order_prex}


def get_query_num_resq(spec: QuerySpec) -> int:
  if spec.exchange == "Binance":
    if len(spec.native_symbols) < 10:  # 40  3
      return 10 * 3

    if len(spec.native_symbols) == 0:
      return 40

    return 40

  else:
    30  # defaut


def detector_open_order(spec: QuerySpec):
  if spec.exchange in callback_map:
    return callback_map[spec.exchange](spec)
  else:
    Exception("Unsupport exchange")
